package com.spring.redis.operation.serializer;

import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTDecoder;
import org.nustaq.serialization.FSTEncoder;
import org.nustaq.serialization.coders.FSTStreamDecoder;
import org.nustaq.serialization.coders.FSTStreamEncoder;

import java.io.IOException;
import java.lang.reflect.Field;

/**
 * @author zhouyu
 */
public class FstDefaultStreamCoderFactory implements FSTConfiguration.StreamCoderFactory {

    Field chBufField;

    Field ascStringCacheField;

    private final FSTConfiguration fstConfiguration;

    private static final ThreadLocal INPUT = new ThreadLocal();

    private static final ThreadLocal OUTPUT = new ThreadLocal();

    {
        try {
            chBufField = FSTStreamDecoder.class.getDeclaredField("chBufS");
            ascStringCacheField = FSTStreamDecoder.class.getDeclaredField("ascStringCache");
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        ascStringCacheField.setAccessible(true);
        chBufField.setAccessible(true);
    }

    FstDefaultStreamCoderFactory(FSTConfiguration fstConfiguration) {
        this.fstConfiguration = fstConfiguration;
    }

    @Override
    public FSTEncoder createStreamEncoder() {
        return new FSTStreamEncoder(fstConfiguration);
    }

    @Override
    public FSTDecoder createStreamDecoder() {
        return new FSTStreamDecoder(fstConfiguration) {
            @Override
            public String readStringUTF() throws IOException {
                try {
                    String res = super.readStringUTF();
                    chBufField.set(this, null);
                    return res;
                } catch (Exception e) {
                    throw new IOException(e);
                }
            }
            @Override
            public String readStringAsc() throws IOException {
                try {
                    String res = super.readStringAsc();
                    ascStringCacheField.set(this, null);
                    return res;
                } catch (Exception e) {
                    throw new IOException(e);
                }
            }
        };
    }


    @Override
    public ThreadLocal getInput() {
        return INPUT;
    }

    @Override
    public ThreadLocal getOutput() {
        return OUTPUT;
    }
}
